﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Proxy;
using Newtonsoft.Json.Serialization;
using System.Reflection;
using Newtonsoft.Json;

namespace FantastPlatform.Framework.Common
{
    public class NHibernateContractResolver : DefaultContractResolver
    {
        //private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers();

        //protected override List<MemberInfo> GetSerializableMembers(Type objectType)
        //{
        //    var members = base.GetSerializableMembers(objectType);

        //    members.RemoveAll(memberInfo =>
        //                      (IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
        //                      (IsMemberDynamicProxyMixin(memberInfo)) ||
        //                      (IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
        //                      (IsMemberInheritedFromProxySuperclass(memberInfo, objectType)));

        //    var actualMemberInfos = new List<MemberInfo>();

        //    foreach (var memberInfo in members)
        //    {
        //        var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
        //        actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]);
        //    }

        //    return actualMemberInfos;
        //}

        //private static bool IsMemberDynamicProxyMixin(MemberInfo memberInfo)
        //{
        //    return memberInfo.Name == "__interceptors";
        //}

        //private static bool IsMemberInheritedFromProxySuperclass(MemberInfo memberInfo, Type objectType)
        //{
        //    return memberInfo.DeclaringType.Assembly == typeof(INHibernateProxy).Assembly;
        //}

        //private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType)
        //{
        //    var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType)
        //                  ? objectType.BaseType.GetMember(memberInfo.Name)
        //                  : objectType.GetMember(memberInfo.Name);

        //    return infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0;
        //}

        //private static bool IsMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo)
        //{
        //    return Array.Exists(NHibernateProxyInterfaceMembers, mi => memberInfo.Name == mi.Name);
        //}
        private string[] exceptMemberName;//例外
        private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers();
        public NHibernateContractResolver()
        {

        }
        public NHibernateContractResolver(string[] exceptMemberName)
        {
            this.exceptMemberName = exceptMemberName;
        }
        protected override List<MemberInfo> GetSerializableMembers(Type objectType)
        {
            var members = new List<PropertyInfo>(objectType.GetProperties());
            members.RemoveAll(memberInfo =>
                //　(IsMemberNames(memberInfo))||
              (IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
              (IsMemberDynamicProxyMixin(memberInfo)) ||
              (IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
              (IsInheritedIList(memberInfo)) ||
              (IsInheritedEntity(memberInfo))
              );
            members = Distinct(members);
            var actualMemberInfos = new List<MemberInfo>();
            foreach (var memberInfo in members)
            {
                var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
                actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]);
                //Debug.WriteLine(memberInfo.Name);
            }
            return actualMemberInfos;
        }
        private static bool IsMemberDynamicProxyMixin(PropertyInfo memberInfo)
        {
            return memberInfo.Name == "__interceptors";
        }
        private static bool IsMemberPartOfNHibernateProxyInterface(PropertyInfo memberInfo)
        {
            bool b = Array.Exists(NHibernateProxyInterfaceMembers, mi => memberInfo.Name == mi.Name);
            return b;
        }
        private static bool IsMemberMarkedWithIgnoreAttribute(PropertyInfo memberInfo, Type objectType)
        {
            var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType) ?
              objectType.BaseType.GetMember(memberInfo.Name) :
              objectType.GetMember(memberInfo.Name);
            bool b = infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0;
            return b;
        }
        private bool IsExceptMember(PropertyInfo memberInfo)
        {
            if (exceptMemberName == null)
                return false;
            return Array.Exists(exceptMemberName, i => memberInfo.Name == i);
        }
        private bool IsInheritedIList(PropertyInfo memberInfo)
        {
            bool b = (memberInfo.PropertyType.Name == "IList`1" && !IsExceptMember(memberInfo));
            return b;
        }
        private bool IsInheritedEntity(PropertyInfo memberInfo)
        {
            bool b = (FindBaseType(memberInfo.PropertyType).Name == "Entity" && !IsExceptMember(memberInfo));
            return b;
        }
        private static Type FindBaseType(Type type)
        {
            if (!type.IsClass)
                return type;
            if (type.Name == "Entity" || type.Name == "Object")
            {
                return type;
            }
            return FindBaseType(type.BaseType);
        }
        private List<PropertyInfo> Distinct(List<PropertyInfo> list)
        {
            List<PropertyInfo> result = new List<PropertyInfo>();
            List<string> names = new List<string>();
            foreach (PropertyInfo p in list)
            {
                if (!names.Contains(p.Name))
                {
                    names.Add(p.Name);
                    result.Add(p);
                }
            }
            return result;
        }

    } 
}
